using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using UnityEngine.Networking;

namespace Framework
{
    public static class FileHelper
    {
        private static readonly System.Text.Encoding _UTF8Encoding = new System.Text.UTF8Encoding(false);

        public static string EnsureDataExt(string path)
        {
            if (path.LastIndexOf(FrameworkSettings.DataExt, StringComparison.Ordinal) >= 0)
            {
                return path;
            }
            return path + FrameworkSettings.DataExt;
        }
        
        /// <summary>
        /// 统一路径中的分隔符
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static string GetUnityPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return path;
            return path.Replace("\\", "/");
        }

        /// <summary>
        /// 获取路径的文件夹名字
        /// </summary>
        /// <param name="assetPath">文件路径</param>
        /// <returns>文件夹名字</returns>
        public static string GetDirNameByAssetPath(string assetPath)
        {
            if (string.IsNullOrEmpty(assetPath))
            {
                Logger.LogError("FileHelper GetDirNameByAssetPath assetPath is null or empty! ");
                return string.Empty;
            }

            string filePath = Path.GetDirectoryName(assetPath);
            if (string.IsNullOrEmpty(filePath))
            {
                return string.Empty;
            }
            filePath = GetUnityPath(filePath);
            string[] dirs = filePath.Split('/');
            if (dirs.Length < 1)
            {
                return string.Empty;
            }
            return dirs[dirs.Length - 1];
        }

        /// <summary>
        /// 将文本信息写入到文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="content">文本内容</param>
        public static void WriteTextToPath(string filePath, string content)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper WriteTextToPath filePath is null!");
                return;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            EnsureDirForFile(filePath);
            try
            {
                File.WriteAllText(filePath, content, _UTF8Encoding);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper WriteTextToPath Exception, filePath = " + filePath);
                Logger.LogException(exp);
            }
        }

        /// <summary>
        /// 将二进制数写入某个文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="data">数据</param>
        public static void WriteAllBytesToPath(string filePath, byte[] data)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("Filepath is null or empty!");
                return;
            }

            if (null == data)
            {
                Logger.LogError("WriteAllBytesToPath data can not be null!");
                return;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            EnsureDirForFile(filePath);
            try
            {
                File.WriteAllBytes(filePath, data);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper WriteAllBytesToPath Exception, filePath = " + filePath);
                Logger.LogException(exp);
            }
        }

        /// <summary>
        /// 将文本内容追加到某个文件最后
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="content">内容</param>
        public static void AppendText(string filePath, string content)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper AppendText, filePath is null!");
                return;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            EnsureDirForFile(filePath);
            try
            {
                File.AppendAllText(filePath, content, _UTF8Encoding);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper AppendText Exception, filePath = " + filePath);
                Logger.LogException(exp);
            }
        }

        /// <summary>
        /// 读取文本文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>返回文本内容</returns>
        public static string ReadText(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper ReadText, filePath is null!");
                return string.Empty;
            }
            if (!IsFileExists(filePath))
            {
                return string.Empty;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            return File.ReadAllText(filePath, _UTF8Encoding);
        }
        /// <summary>
        /// 读取二进制文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>文件内容</returns>
        public static byte[] ReadBytes(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper ReadBytes, filePath is null!");
                return null;
            }
            if (!IsFileExists(filePath))
            {
                return null;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            return File.ReadAllBytes(filePath);
        }

        /// <summary>
        /// 删除某个目录下的文件和文件夹
        /// </summary>
        /// <param name="dirPath">目录</param>
        public static void ClearDir(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                Logger.LogWarning("FileHelper ClearDir, dirPath is null!");
                return;
            }
            dirPath = FileHelper.GetUnityPath(dirPath);
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
            if (dirInfo.Exists)
            {
                try
                {
                    dirInfo.Delete(true);
                }
                catch (System.Exception exp)
                {
                    Logger.LogError("FileHelper ClearDir Exception, dirPath = " + dirPath);
                    Logger.LogException(exp);
                }
            }
        }

        /// <summary>
        /// 清理某个目录
        /// 可以设置保留目录和保留文件
        /// </summary>
        /// <param name="dirPath">目录路径</param>
        /// <param name="ignoreDirs">保留文件夹配置</param>
        /// <param name="ignoreFiles">保留文件配置</param>
        public static void ClearDirWithIgnore(string dirPath, List<string> ignoreDirs = null, List<string> ignoreFiles = null)
        {
            if (null == ignoreDirs && null == ignoreFiles)
            {
                ClearDir(dirPath);
                return;
            }
            if (string.IsNullOrEmpty(dirPath))
            {
                Logger.LogWarning("FileHelper ClearDir, dirPath is null!");
                return;
            }
            if (!Directory.Exists(dirPath))
            {
                return;
            }
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
            string unityPath = string.Empty;
            foreach (FileInfo item in dirInfo.GetFiles("*.*", SearchOption.AllDirectories))
            {
                if (_IsFileIgnored(item.FullName, ignoreDirs, ignoreFiles))
                {
                    //Logger.Log($"_IsFileIgnored item.FullName = {item.FullName}");
                    continue;
                }
                DeleteFile(item.FullName);
                var fileDir = Path.GetDirectoryName(item.FullName);
                if (IsDirEmpty(fileDir))
                {
                    Directory.Delete(fileDir);
                }
                //Logger.Log($"DeleteFile item.FullName = {item.FullName} fileDir = {fileDir} !");
            }
        }
        /// <summary>
        /// 文件是否被忽略
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="ignoreDirs">保留文件夹配置</param>
        /// <param name="ignoreFiles">保留文件配置</param>
        /// <returns></returns>
        private static bool _IsFileIgnored(string filePath, List<string> ignoreDirs, List<string> ignoreFiles)
        {
#if !(UNITY_ANDROID || UNITY_IOS)
            File.SetAttributes(filePath, FileAttributes.Normal);
#endif
            string fileUnityPath = GetUnityPath(filePath);
            if (null != ignoreDirs)
            {
                string dirUnityPath;
                foreach (string dirPath in ignoreDirs)
                {
                    dirUnityPath = GetUnityPath(dirPath);
                    if (fileUnityPath.Contains(dirUnityPath))
                    {
                        return true;
                    }
                }
            }
            if (null != ignoreFiles)
            {
                string unityPath;
                foreach (var item in ignoreFiles)
                {
                    unityPath = GetUnityPath(item);
                    if (unityPath.Equals(filePath))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 删除路径上的文件
        /// </summary>
        /// <param name="filePath"></param>
        public static void DeleteFile(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("filePath is null!");
                return;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            if (IsFileExists(filePath))
            {
                try
                {
                    File.Delete(filePath);
                }
                catch (System.Exception exp)
                {
                    Logger.LogError("FileHelper DeleteFile Exception, filePath = " + filePath);
                    Logger.LogException(exp);
                }
            }
        }

        /// <summary>
        /// 判断某个文件目录是否存在，不存在则创建之，保证某个目录存在
        /// </summary>
        /// <param name="dirPath">目录</param>
        public static void EnsureDir(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                Logger.LogWarning("FileHelper EnsureDir, dirPath is null!");
                return;
            }
            dirPath = FileHelper.GetUnityPath(dirPath);

            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
        }

        public static bool IsDirExists(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                Logger.LogWarning("FileHelper IsDirExists, dirPath is null!");
                return false;
            }
            return Directory.Exists(dirPath);
        }

        public static bool IsDirEmpty(string dirPath)
        {
            if (IsDirExists(dirPath))
            {
                return (Directory.GetFiles(dirPath).Length == 0 && Directory.GetDirectories(dirPath).Length == 0);
            }
            return false;
        }

        /// <summary>
        /// 确保某个文件路径的文件夹存在
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public static void EnsureDirForFile(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper EnsureDirForFile, filePath is null!");
                return;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            string dirPath = filePath.Substring(0, filePath.LastIndexOf("/"));
            EnsureDir(dirPath);
        }

        /// <summary>
        /// 判断文件是否存在
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns></returns>
        public static bool IsFileExists(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                Logger.LogWarning("FileHelper IsFileExists, filePath is null or empty!");
                return false;
            }
            filePath = FileHelper.GetUnityPath(filePath);
            return File.Exists(filePath);
        }

        public static long GetFileSize(string path)
        {
            long size = 0;
            if (IsFileExists(path))
            {
                using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    size = fs.Length;
                    fs.Close();
                }
            }
            return size;
        }

        /// <summary>
        /// 将srcDir目录下的东西拷贝到desDir目录下
        /// </summary>
        /// <param name="srcDir">源目录</param>
        /// <param name="desDir">目的目录</param>
        public static void CopyDirectory(string srcDir, string desDir)
        {
            srcDir = FileHelper.GetUnityPath(srcDir);
            desDir = FileHelper.GetUnityPath(desDir);
            if (!Directory.Exists(desDir))
            {
                Directory.CreateDirectory(desDir);
            }

            //string folderName = desDir.Substring(desDir.LastIndexOf("/") + 1);
            string[] fileNames = Directory.GetFileSystemEntries(srcDir);
            string realFile = string.Empty;
            int idx = 0;
            foreach (string file in fileNames)
            {
                realFile = FileHelper.GetUnityPath(file);
                if (!File.Exists(realFile))
                {
                    string newDesDir = desDir + "/" + realFile.Substring(realFile.LastIndexOf('/') + 1);
                    CopyDirectory(realFile, newDesDir);
                    continue;
                }
                idx++;
                string srcFileName = realFile.Substring(realFile.LastIndexOf("/") + 1);
                File.Copy(realFile, desDir + "/" + srcFileName, true);
            }
        }

        /// <summary>
        /// 修改目录或者文件的名字
        /// </summary>
        /// <param name="srcPath">路径,文件路径or文件夹路径</param>
        /// <param name="newName">新名字</param>
        public static void ChangeDirOrFileName(string srcPath, string newName)
        {
            srcPath = FileHelper.GetUnityPath(srcPath);
            if (!Directory.Exists(srcPath) && !File.Exists(srcPath))
            {
                Logger.LogError("Directory or file: " + srcPath + " is not exists!");
                return;
            }
            string parentDirName = srcPath.Substring(0, srcPath.LastIndexOf("/") + 1);
            string newPath = parentDirName + newName;

            if (Directory.Exists(srcPath))
            {
                MoveDir(srcPath, newPath);
                return;
            }
            MoveFile(srcPath, newPath);
        }

        /// <summary>
        /// 移动一个文件夹
        /// </summary>
        /// <param name="srcDir">原来的路径</param>
        /// <param name="dstDir">移动之后的路径</param>
        public static void MoveDir(string srcDir, string dstDir)
        {
            if (string.IsNullOrEmpty(srcDir) || string.IsNullOrEmpty(dstDir))
            {
                Logger.LogError($"FileHelper MoveDir , srcDir = {srcDir} or dstDir = {dstDir} is null!");
                return;
            }
            if (!Directory.Exists(srcDir))
            {
                Logger.LogError($"FileHelper MoveDir , srcDir = {srcDir} is not exists!");
                return;
            }
            try
            {
                Directory.Move(srcDir, dstDir);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper MoveDir Directory Exception, srcDirPath = " + srcDir + " dstDir = " + dstDir);
                Logger.LogException(exp);
            }
        }
        /// <summary>
        /// 移动一个文件
        /// </summary>
        /// <param name="srcPath">原来的路径</param>
        /// <param name="dstPath">移动之后的路径</param>
        public static void MoveFile(string srcPath, string dstPath)
        {
            if (string.IsNullOrEmpty(srcPath) || string.IsNullOrEmpty(dstPath))
            {
                Logger.LogError($"FileHelper MoveFile , srcPath = {srcPath} or dstPath = {dstPath} is null!");
                return;
            }
            if (!File.Exists(srcPath))
            {
                Logger.LogError($"FileHelper MoveFile , srcPath = {srcPath} is not exists!");
                return;
            }
            if (File.Exists(dstPath))
            {
                File.Delete(dstPath);
            }
            try
            {
                File.Move(srcPath, dstPath);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper MoveFile File Exception, srcPath = " + srcPath + " dstPath = " + dstPath);
                Logger.LogException(exp);
            }
        }

        /// <summary>
        /// 复制一个文件
        /// </summary>
        /// <param name="srcPath">源文件路径</param>
        /// <param name="dstPath">目标路径</param>
        /// <param name="overwrite">是否覆盖目标文件</param>
        public static void CopyFile(string srcPath, string dstPath, bool overwrite = false)
        {
            if (string.IsNullOrEmpty(srcPath) || string.IsNullOrEmpty(dstPath))
            {
                Logger.LogError($"FileHelper CopyFile , srcPath = {srcPath} or dstPath = {dstPath} is null!");
                return;
            }
            if (!File.Exists(srcPath))
            {
                Logger.LogError($"FileHelper CopyFile , srcPath = {srcPath} is not exists!");
                return;
            }
            EnsureDirForFile(dstPath);
            try
            {
                File.Copy(srcPath, dstPath, overwrite);
            }
            catch (System.Exception exp)
            {
                Logger.LogError("FileHelper CopyFile File Exception, srcPath = " + srcPath + " dstPath = " + dstPath);
                Logger.LogException(exp);
            }
        }

        /// <summary>
        /// 获取目录下所有子文件夹
        /// </summary>
        /// <param name="dirPath">路径</param>
        /// <returns></returns>
        public static string[] GetSubdirectories(string dirPath)
        {
            return Directory.GetDirectories(dirPath);
        }

        /// <summary>
        /// 获取目录下所有文件路径+文件名+扩展名
        /// </summary>
        /// <param name="dirPath">路径</param>
        /// <param name="allDirectories">是否递归遍历所有子文件夹</param>
        /// <returns></returns>
        public static string[] GetDirFilePaths(string dirPath, bool allDirectories = true)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                Logger.LogWarning("GetDirFilePaths fail, dirPath is null!");
                return null;
            }
            SearchOption option = SearchOption.TopDirectoryOnly;
            if (allDirectories)
            {
                option = SearchOption.AllDirectories;
            }
            dirPath = FileHelper.GetUnityPath(dirPath);
            if (!Directory.Exists(dirPath))
            {
                return null;
            }
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
            var fileInfos = dirInfo.GetFiles("*.*", option);
            string[] res = new string[fileInfos.Length];
            for (int i = 0; i < fileInfos.Length; ++i)
            {
                res[i] = FileHelper.GetUnityPath(fileInfos[i].FullName);
            }
            return res;
        }

        /// <summary>
        /// 根据路径，获取文件名
        /// </summary>
        /// <param name="path"></param>
        /// <param name="hasExt"></param>
        public static string GetFileName(string path, bool hasExt)
        {
            string ans = null;
            if (hasExt)
            {
                ans = Path.GetFileName(path);
            }
            else
            {
                ans = Path.GetFileNameWithoutExtension(path);
            }
            return ans;
        }


        private const int EnHashLength = 48;
        private static byte[] enHashCodeBuffer = new byte[EnHashLength];
        /// <summary>
        /// 验证某些配置文件
        /// </summary>
        /// <param name="data">文件内容</param>
        /// <returns></returns>
        public static byte[] RsaVerity(byte[] data)
        {
            if (null == data || data.Length < EnHashLength)
            {
                Logger.LogError("FileHelper.RsaVerity, data can not be null or length valid!");
                return null;
            }
            System.Array.Copy(data, 0, enHashCodeBuffer, 0, EnHashLength);
            string signedMd5 = StringHelper.ByteArray2Base64Str(enHashCodeBuffer);
            var realLength = data.Length - EnHashLength;
            var md5Sign = MD5Util.ComputeMd5Sign(data, EnHashLength, realLength);
            if (RSAUtil.RSASignCheck(md5Sign, signedMd5, GameConfig.RsaPublicKey))
            {
                string retStr = MD5Util.AESDecrypt(data, EnHashLength, realLength, GameConfig.AESPassword, GameConfig.AESKeyVector);
                return StringHelper.Str2ByteArray(retStr);
            }
            return null;
        }


        /// <summary>
        /// RSA签名验证
        /// </summary>
        /// <param name="data">文件内容</param>
        /// <returns>0:校验失败 大于0：校验成功，返回有效数据大小</returns>
        public static int RsaJustVerity(byte[] data)
        {
            if (null == data || data.Length < EnHashLength)
            {
                Logger.LogError("FileHelper.RsaJustVerity, data can not be null or length valid!");
                return 0;
            }
            System.Array.Copy(data, 0, enHashCodeBuffer, 0, EnHashLength);
            string signedMd5 = StringHelper.ByteArray2Base64Str(enHashCodeBuffer);
            var realLength = data.Length - EnHashLength;
            var md5Sign = MD5Util.ComputeMd5Sign(data, EnHashLength, realLength);
            if (RSAUtil.RSASignCheck(md5Sign, signedMd5, GameConfig.RsaPublicKey))
            {
                return realLength;
            }
            Logger.LogError("FileHelper.RsaJustVerity, RSA Sign Check failed!");
            return 0;
        }

        /// <summary>
        /// RSA签名验证
        /// </summary>
        /// <param name="data">文件内容</param>
        /// <returns>0:校验失败 大于0：校验成功，返回有效数据大小</returns>
        [LuaInterface.NoToLua]
        public static int RsaJustVerityRunTime(byte[] data)
        {
            if (null == data || data.Length < EnHashLength)
            {
                Logger.LogError("FileHelper.RsaJustVerity, data can not be null or length valid!");
                return 0;
            }
            System.Array.Copy(data, 0, enHashCodeBuffer, 0, EnHashLength);
            string signedMd5 = StringHelper.ByteArray2Base64Str(enHashCodeBuffer);
            var realLength = data.Length - EnHashLength;
            var md5Sign = MD5Util.ComputeMd5Sign(data, EnHashLength, realLength);
            if (RSAUtil.RSASignCheck(md5Sign, signedMd5, @"<RSAKeyValue><Modulus>lqlSywySAeANv8iLKwb7/6R4YM7FHt6KLPILqrQXfWctVauiMdj2TzE96dN1hBqt</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"))
            {
                return realLength;
            }
            Logger.LogError("FileHelper.RsaJustVerity, RSA Sign Check failed!");
            return 0;
        }

        public static IEnumerator CopyFromStreamToPersist(string srcPath, string dstPath, System.Action<bool, object> callBack, object passObj)
        {
            FileHelper.DeleteFile(dstPath);
            FileHelper.EnsureDirForFile(dstPath);
            using (UnityWebRequest fileRequest = new UnityWebRequest(srcPath))
            {
                CopyDownloadHandler copyDownloadHandler = new CopyDownloadHandler(dstPath);
                fileRequest.downloadHandler = copyDownloadHandler;
                yield return fileRequest.SendWebRequest();

                if (fileRequest.isHttpError || fileRequest.isNetworkError)
                {
                    Logger.LogError($"FileHelper.CopyFromStreamToPersist, read file srcPath = {srcPath} error = {fileRequest.error} !");
                    callBack?.Invoke(false, passObj);
                }
                else
                {
                    callBack?.Invoke(true, passObj);
                }
                // Logger.Log($"FileHelper.CopyFromStreamToPersist, srcPath = {srcPath} dstPath = {srcPath} finish !");
                copyDownloadHandler.MannualDispose();
            }
        }

    }

    internal class CopyDownloadHandler : DownloadHandlerScript
    {
        private FileStream writeStream;
        private readonly string filePath;
        public CopyDownloadHandler(string dstPath) : base(new byte[100 * 1024])
        {
            filePath = dstPath;
        }
        protected override bool ReceiveData(byte[] data, int dataLength)
        {
            if (data == null || dataLength == 0)
            {
                return false;
            }
            if (null == writeStream)
            {
                writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
            }
            try
            {
                writeStream.Write(data, 0, dataLength);
            }
            catch (Exception exp)
            {
                Logger.LogError(exp.Message);
                return false;
            }
            return true;
        }

        private void Clear()
        {
            if (null != writeStream)
            {
                writeStream.Flush();
                writeStream.Close();
                writeStream = null;
            }
        }

        public void MannualDispose()
        {
            Dispose();
            Clear();
        }

        protected override void CompleteContent()
        {
            base.CompleteContent();
            Clear();
        }

        ~CopyDownloadHandler()
        {
            Clear();
        }
    }
}
